﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using DaVikingCode.AssetPacker;
using SoarD.Pandora;
using SoarDEditor.Pandora;
using UnityEditor;
using UnityEngine;
using Object = UnityEngine.Object;

public class AtlasMaker
{
    static string PATH_TEMP_ATLAS = PdrConfig.Instance.UIAtlasTempPath;
    static string PATH_ATLAS = PdrConfig.Instance.UIAtlasPath;

    public static void MakeAtlas(Object obj, bool isCompress, bool isSplitChannel)
    {
        string sorPath = AssetDatabase.GetAssetPath(obj);
        string name = Path.GetFileNameWithoutExtension(sorPath);
        ExportAtlas(sorPath, name, isCompress, isSplitChannel);
    }

    private static void ExportAtlas(string pathSrc, string name, bool isCompress = true, bool isSplitChannel = true)
    {
        if (!pathSrc.Contains("Data"))
        {
            Debug.LogError("文件路径错误");
            return;
        }

        bool trim = true;
        int totalCount = 5;
        int current = 0;
        string pathTmp = PATH_TEMP_ATLAS;
        string pathDst = PATH_ATLAS;

        PlayProgressBar(name, "MAKE_ATLAS", (++current) / totalCount);
        MakeAtlas(name, pathSrc, pathTmp, trim);

        PlayProgressBar(name, "SPLIT_CHANNEL", (++current) / totalCount);
        SplitChannel(name, pathTmp, pathDst, isCompress, isSplitChannel);

        PlayProgressBar(name, "CREATE_MATERIAL", (++current) / totalCount);
        CreateMaterial(name, pathDst, true, isSplitChannel);

        PlayProgressBar(name, "IMPORT_SPRITE", (++current) / totalCount);
        ImportSprite(name, pathSrc, pathTmp, pathDst, isCompress);

        PlayProgressBar(name, "CREATE_PREFAB", (++current) / totalCount);
        CreateSpritePrefab(name, pathDst, pathTmp);

        ClearProgressBar();
    }

    public static void MakeAtlas(string name, string pathSrc, string pathDst, bool trim)
    {
        AtlasPacker.MakeAtlas(pathSrc, pathDst, name);
        AssetDatabase.Refresh();

        string pathPng = string.Format("{0}/{1}.png", pathDst, name);
        TextureImporter importer = (TextureImporter)AssetImporter.GetAtPath(pathPng);
        importer.textureType = TextureImporterType.Default;
        importer.isReadable = true;
        importer.mipmapEnabled = false;
        importer.alphaSource = TextureImporterAlphaSource.FromInput;
        importer.maxTextureSize = 2048;
        importer.textureCompression = TextureImporterCompression.Uncompressed;
        importer.SaveAndReimport();
        AssetDatabase.ImportAsset(pathPng, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
        AssetDatabase.Refresh();
    }

    public static void SplitChannel(string name, string pathSrc, string pathDst, bool isCompress, bool isSplitChannel)
    {
        string path = string.Format("{0}/{1}.png", pathSrc, name);
        string pathColor = string.Format("{0}/{1}_c.png", pathDst, name);
        string pathAlpha = string.Format("{0}/{1}_a.png", pathDst, name);
        string pathTpSheet = string.Format("{0}/{1}_c.tpsheet", pathDst, name);
        if (File.Exists(pathTpSheet))
        {
            File.Delete(pathTpSheet);
        }

        if (!Directory.Exists(pathDst))
        {
            Directory.CreateDirectory(pathDst);
        }

        Texture2D texture = AssetDatabase.LoadAssetAtPath<Texture2D>(path);
        TextureImporter importTex = (TextureImporter)AssetImporter.GetAtPath(path);
        if (importTex == null)
        {
            Debug.LogError(path + "is null");
            return;
        }
        bool isChangeImporter = false;
        if (!importTex.isReadable)
        {
            importTex.isReadable = true;
            isChangeImporter = true;
        }
        if (importTex.alphaSource != TextureImporterAlphaSource.FromInput)
        {
            importTex.alphaSource = TextureImporterAlphaSource.FromInput;
            isChangeImporter = true;
        }
        if (isChangeImporter)
        {
            importTex.SaveAndReimport();
        }
        if (isSplitChannel)
        {
            Texture2D textureColor = new Texture2D(texture.width, texture.height);
            Texture2D textureAlpha = new Texture2D(texture.width, texture.height);

            for (int i = 0; i < texture.width; ++i)
            {
                for (int j = 0; j < texture.height; ++j)
                {
                    Color color = texture.GetPixel(i, j);
                    textureAlpha.SetPixel(i, j, new Color(color.a, color.a, color.a));
                    textureColor.SetPixel(i, j, new Color(color.r, color.g, color.b));
                }
            }

            File.WriteAllBytes(pathColor, textureColor.EncodeToPNG());
            File.WriteAllBytes(pathAlpha, textureAlpha.EncodeToPNG());
        }
        else
        {
            Texture2D textureColor = new Texture2D(texture.width, texture.height);

            for (int i = 0; i < texture.width; ++i)
            {
                for (int j = 0; j < texture.height; ++j)
                {
                    Color color = texture.GetPixel(i, j);
                    textureColor.SetPixel(i, j, new Color(color.r, color.g, color.b, color.a));
                }
            }
            File.WriteAllBytes(pathColor, textureColor.EncodeToPNG());
            if (File.Exists(pathAlpha))
            {
                File.Delete(pathAlpha);
            }
        }

        AssetDatabase.Refresh();

        if (isSplitChannel)
        {
            TextureImporter importAlpha = (TextureImporter)AssetImporter.GetAtPath(pathAlpha);
            importAlpha.textureType = TextureImporterType.Default;
            importAlpha.mipmapEnabled = false;
            importAlpha.isReadable = false;
            importAlpha.alphaSource = TextureImporterAlphaSource.None;
            importAlpha.textureCompression = isCompress ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed;
            importAlpha.maxTextureSize = texture.width / AtlasConfig.GetAtlasAphlaScale(name);
            importAlpha.filterMode = FilterMode.Bilinear;
            importAlpha.SaveAndReimport();
        }
        else
        {
            TextureImporter importColor = (TextureImporter)AssetImporter.GetAtPath(pathColor);
            importColor.textureType = TextureImporterType.Default;
            importColor.mipmapEnabled = false;
            importColor.isReadable = false;
            importColor.alphaSource = TextureImporterAlphaSource.FromInput;
            importColor.textureCompression = isCompress ? TextureImporterCompression.Compressed : TextureImporterCompression.Uncompressed;
            importColor.maxTextureSize = texture.width;
            importColor.filterMode = FilterMode.Bilinear;
            importColor.SaveAndReimport();
        }
        AssetDatabase.Refresh();
    }

    static void CreateMaterial(string name, string pathDst, bool isUI, bool isSplitChannel)
    {
        string pathColor = string.Format("{0}/{1}_c.png", pathDst, name);
        string pathAlpha = string.Format("{0}/{1}_a.png", pathDst, name);
        string pathMaterial = string.Format("{0}/{1}_mat.mat", pathDst, name);

        Texture2D textureColor = AssetDatabase.LoadAssetAtPath<Texture2D>(pathColor);
        Texture2D textureAlpha = AssetDatabase.LoadAssetAtPath<Texture2D>(pathAlpha);

        string shaderName;
        if (isUI)
        {
            if (isSplitChannel)
                shaderName = "UI/Default(RGB+A)";
            else
                shaderName = "UI/Default(ARGB)";
        }
        else
            shaderName = "Custom/Transparent/Diffuse(RGB+A)_Lambert";
        Shader shader = Shader.Find(shaderName);
        Material material;
        if (File.Exists(pathMaterial))
        {
            material = AssetDatabase.LoadAssetAtPath<Material>(pathMaterial);
            material.shader = shader;
        }
        else
        {
            material = new Material(shader);
        }
        //todo: 真彩的时候这里是错的要改...
        material.SetTexture("_MainTex", textureColor);
        material.SetTexture("_AlphaTex", textureAlpha);

        if (File.Exists(pathMaterial))
        {
            EditorUtility.SetDirty(material);
            AssetDatabase.SaveAssets();
        }
        else
        {
            AssetDatabase.CreateAsset(material, pathMaterial);
        }
    }

    public static void ImportSprite(string name, string pathSrc, string pathTP, string pathDst, bool isCompress)
    {
        string pathTxt = string.Format("{0}/{1}.json", pathTP, name);
        string text = PdrFileUtil.ReadTextFromFile(pathTxt);

        string pathTexture = string.Format("{0}/{1}_c.png", pathDst, name);
        TextureImporter importer = AssetImporter.GetAtPath(pathTexture) as TextureImporter;

        List<SpriteMetaData> sprites = ProcessToSprites(text);
        RewriteInfo(sprites, pathSrc);
        importer.spritesheet = sprites.ToArray();
        importer.textureType = TextureImporterType.Sprite;
        importer.spriteImportMode = SpriteImportMode.Multiple;
        importer.sRGBTexture = true;
        importer.filterMode = FilterMode.Bilinear;
        importer.spritePackingTag = null;
        importer.mipmapEnabled = false;
        importer.isReadable = false;
        importer.maxTextureSize = 2048;
        if (isCompress)
        {
            importer.alphaSource = TextureImporterAlphaSource.None;
            importer.textureCompression = TextureImporterCompression.Compressed;
        }
        else
        {
            importer.alphaSource = TextureImporterAlphaSource.FromInput;
            importer.textureCompression = TextureImporterCompression.Uncompressed;
        }
        importer.SaveAndReimport();
        AssetDatabase.ImportAsset(pathTexture, ImportAssetOptions.ForceUpdate | ImportAssetOptions.ForceSynchronousImport);
        AssetDatabase.Refresh();
    }

    public static List<SpriteMetaData> ProcessToSprites(string text)
    {
        List<SpriteMetaData> spriteMetaDataList = new List<SpriteMetaData>();
        TextureAssets textureAssets = JsonUtility.FromJson<TextureAssets>(text);
        foreach (TextureAsset textureAsset in textureAssets.assets)
        {
            SpriteMetaData spriteData = new SpriteMetaData();
            spriteData.name = textureAsset.name;
            spriteData.rect = new Rect(textureAsset.x, textureAsset.y, textureAsset.width, textureAsset.height);
            spriteData.pivot = new Vector2(0.5f, 0.5f);
            spriteMetaDataList.Add(spriteData);
        }
        return spriteMetaDataList;
    }

    private static void RewriteInfo(List<SpriteMetaData> sprites, string pathSrc)
    {
        string[] strDirectorys = null;
        for (int i = 0; i < sprites.Count; ++i)
        {
            SpriteMetaData data = sprites[i];
            string pathTexture = string.Format("{0}/{1}.png", pathSrc, data.name);
            TextureImporter importer = AssetImporter.GetAtPath(pathTexture) as TextureImporter;
            if (importer == null)
            {
                // 取不到，可能是有子目录，从子目录再找下看看
                if (strDirectorys == null)
                    strDirectorys = GetTopDirectory(pathSrc);

                if (strDirectorys == null)
                    continue;

                for (int j = 0; j < strDirectorys.Length; ++j)
                {
                    pathTexture = string.Format("{0}/{1}/{2}.png", pathSrc, strDirectorys[j], data.name);
                    importer = AssetImporter.GetAtPath(pathTexture) as TextureImporter;
                    if (importer)
                    {
                        break;
                    }
                }

                if (importer == null)
                    continue;
            }

            data.border = importer.spriteBorder;
            if (data.alignment != 9)
            {
                data.pivot = importer.spritePivot;
            }
            sprites[i] = data;
        }
    }

    // 取子目录
    private static string[] GetTopDirectory(string pathSrc)
    {
        DirectoryInfo direction = new DirectoryInfo(pathSrc);
        FileInfo[] files = direction.GetFiles("*", SearchOption.TopDirectoryOnly);
        if (files.Length <= 0)
            return null;

        string[] strDirectorys = new string[files.Length];
        for (int i = 0; i < files.Length; ++i)
        {
            strDirectorys.SetValue(files[i].Name.Replace(".meta", ""), i);
        }

        return strDirectorys;
    }

    static void CreateSpritePrefab(string name, string path, string pathTP)
    {
        string pathPrefab = string.Format("{0}/{1}.prefab", path, name);
        string pathColor = string.Format("{0}/{1}_c.png", path, name);
        string pathMaterial = string.Format("{0}/{1}_mat.mat", path, name);

        SpriteMetaData meta;
        Dictionary<string, SpriteMetaData> metas = new Dictionary<string, SpriteMetaData>();

        TextureImporter importer = AssetImporter.GetAtPath(pathColor) as TextureImporter;
        for (int i = 0; i < importer.spritesheet.Length; ++i)
        {
            meta = importer.spritesheet[i];
            if (metas.ContainsKey(meta.name))
                Debug.LogError("key is already exist the dictionary:  " + meta.name);
            metas.Add(meta.name, meta);
        }

        List<Sprite> sprites = new List<Sprite>();
        Sprite sprite;
        Object[] objects = AssetDatabase.LoadAllAssetRepresentationsAtPath(pathColor);
        for (int i = 0; i < objects.Length; ++i)
        {
            sprite = objects[i] as Sprite;
            if (sprite != null)
                sprites.Add(sprite);
        }

        GameObject prefab = null;
        GameObject go = null;
        if (File.Exists(pathPrefab))
        {
            prefab = AssetDatabase.LoadAssetAtPath<GameObject>(pathPrefab);
            go = Object.Instantiate(prefab);
        }
        else
        {
            go = new GameObject();
        }

        go.name = name;

        var hierarchy = go.GetComponent<AtlasHierarchy>() ?? go.AddComponent<AtlasHierarchy>();
        Material mat = AssetDatabase.LoadAssetAtPath<Material>(pathMaterial);
        SetAtlasData(hierarchy, sprites, mat);
        if (prefab != null)
            PrefabUtility.ReplacePrefab(go, prefab);
        else
            PrefabUtility.CreatePrefab(pathPrefab, go);
        Object.DestroyImmediate(go);
        AssetDatabase.Refresh();

        BuildOnePaddingData(name, false, hierarchy);
    }

    public static void SetAtlasData(AtlasHierarchy atlashierarchy, List<Sprite> sprites, Material mat)
    {
        atlashierarchy.atlas = new AtlasData();
        atlashierarchy.atlas.sprites = new List<AtlasData.SpriteData>();
        for (int i = 0; i < sprites.Count; i++)
            atlashierarchy.atlas.sprites.Add(new AtlasData.SpriteData(sprites[i]));
        atlashierarchy.atlas.material = mat;
    }

    public static void BuildOnePaddingData(string name, bool isPoly, AtlasHierarchy hierarchy)
    {
        bool needCreate = false;
        string paddingPath = Config.UI_ASSETS_PATH + "/atlas_padding.asset";

        PaddingData data = AssetDatabase.LoadAssetAtPath<PaddingData>(paddingPath);
        if (data == null)
        {
            data = ScriptableObject.CreateInstance<PaddingData>();
            needCreate = true;
        }

        if (data.atlas != null)
        {
            for (int i = data.atlas.Count - 1; i >= 0; i--)
            {
                if (data.atlas[i].name == name)
                    data.atlas.RemoveAt(i);
            }
        }

        List<string> spriteNames = GetSpriteDataName(hierarchy);
        List<Vector4> paddings = GetSpriteDataPadding(hierarchy);

        AddAtlasEx(data, name, isPoly, spriteNames, paddings);
        CheckAtlas(data.atlas);
        if (needCreate)
        {
            AssetDatabase.CreateAsset(data, paddingPath);
        }
        else
        {
            EditorUtility.SetDirty(data);
            AssetDatabase.SaveAssets();
        }
        AssetDatabase.Refresh();
    }

    static void CheckAtlas(List<PaddingData.AtlasInfo> atlas)
    {
        for (int i = atlas.Count - 1; i >= 0; i--)
        {
            string name = atlas[i].name;
            var pathAtlasInfo = string.Format("{0}/{1}.prefab", PdrConfig.Instance.UIAtlasPath, name);
            Debug.Log("pathAtlasInfo:"+ pathAtlasInfo);
            var asset = AssetDatabase.LoadAssetAtPath<AtlasHierarchy>(pathAtlasInfo);
            if (asset == null)
            {
                atlas.Remove(atlas[i]);
                Debug.Log("Remove:" + name);
            }
        }
    }

    public static void AddAtlasEx(PaddingData padding, string name, bool isPoly, List<string> spriteNames, List<Vector4> paddings)
    {
        List<PaddingData.SpriteInfo> SpriteInfoList = new List<PaddingData.SpriteInfo>();
        for (int i = 0; i < spriteNames.Count; i++)
        {
            PaddingData.SpriteInfo sprite = new PaddingData.SpriteInfo(spriteNames[i], paddings[i]);
            SpriteInfoList.Add(sprite);
        }
        padding.AddAtlas(name, isPoly, SpriteInfoList);
    }

    public static List<string> GetSpriteDataName(AtlasHierarchy atlashierarchy)
    {
        List<string> names = new List<string>();
        for (int i = 0; i < atlashierarchy.atlas.sprites.Count; i++)
            names.Add(atlashierarchy.atlas.sprites[i].name);
        return names;
    }

    public static List<Vector4> GetSpriteDataPadding(AtlasHierarchy atlashierarchy)
    {
        List<Vector4> paddings = new List<Vector4>();
        for (int i = 0; i < atlashierarchy.atlas.sprites.Count; i++)
            paddings.Add(atlashierarchy.atlas.sprites[i].padding);
        return paddings;
    }

    public static void SetAtlasData(AtlasHierarchy atlashierarchy, List<Sprite> sprites, List<Vector4> rts, List<bool> trims, Material mat)
    {
        atlashierarchy.atlas = new AtlasData();
        atlashierarchy.atlas.sprites = new List<AtlasData.SpriteData>();
        for (int i = 0; i < sprites.Count; i++)
        {
            atlashierarchy.atlas.sprites.Add(new AtlasData.SpriteData(sprites[i], rts[i], trims[i]));
        }
        atlashierarchy.atlas.material = mat;
    }

    public static void PlayProgressBar(string title, string content, float ratio)
    {
        EditorUtility.DisplayProgressBar(title, content, ratio);
    }
    public static void ClearProgressBar()
    {
        EditorUtility.ClearProgressBar();
    }
}
